本篇的「岔題」在開頭。撰寫鐵人賽系列文章時,我的目標不是完整介紹 Modern C++ 的每一個方面,而是把基本功能與用法,透過簡單的範例,做個粗淺的介紹。C++ 畢竟是資深系統語言,即使範圍限縮在 Modern C++ 完整介紹需要投入相當規模的篇幅。若讀者發現發佈於鐵人賽系列文章只講到皮毛,屬正常現象。
初學 std::shared_ptr<T> 常見幾個疑問:
c<T> ,那麼應該設計成 Call by Value, Call by Reference, Call by Pointer?std::shared_ptr<T> 是不是 thread-safe?Microsoft 過去一年在 C++ 的文件上做了蠻大幅度的更新,關於 std::shared_ptr 的使用,可以參考這篇文件。
「Modern C++」有一個寬鬆的要求:盡可能不要直接使用 new/delete 來建構物件或使用記憶體。遇到 new/delete 應該改用 std::make_unique 或者 std::make_shared 來取代。《Effective Modern C++》Item 21 有說明理由。
第二個理由是為了避免 Memory Leak。考慮以下程式碼:
void Admit1Country2Systems(std::shared_ptr<TeaShopOwner>,
bool yes_or_no);
Admit1Country2Systems(std::shared_ptr<TeaShopOwner>(new TeaShopOwner),
SayYes());
在 C++17 之前,上述函數呼叫可能出現 Memory Leak,原因是 C++11⁄14 標準規格中,沒有規定函數的參數被處理的順序,因此上述的函數呼叫,有可能發生 new TeaShopOwner 完成,但還沒有被放進 std::shared_ptr 管理,結果 SayYes() 裡拋出異常,因此導致 Memory Leak(new TeaShopOwner 沒有刪除)。
由於以上原因,Modern C++ 最佳實務是「對自己好一點」,盡可能使用 std::make_shared 來創建物件。
前述例子中的函數將 std::shared_ptr 參數定義為 By Value。「語義」是:呼叫此函數會將傳入的 std::shared_ptr 物件的「參用計數」加一。如此一來,即使呼叫該函數為非同步操作,也不用擔心 std::shared_ptr 管理的物件被提前刪除。
void Admit1Country2Systems(std::shared_ptr<TeaShopOwner>,
bool yes_or_no);
關於這個問題,多年前多位 C++ 大神曾經回答過,有興趣的讀者可以觀看這支影片。
在場的幾位重量級大神(包含 C++ 之父)針對這個問題也有不同的意見。就現場激起的討論中,我們得到一個結論:C++ 真是一個複雜的程式語言吶。
看了好幾次才看懂這句話的意思:「前述例子中的函數將 std::shared_ptr 參數定義為 By Value。「語義」是:呼叫此函數會將傳入的 std::shared_ptr 物件的「參用計數」加一。」
汗顏
。系列文章還有很多待加強之處,還請多包涵。